// ------------------------
// projects/darwin/main.c++
// Copyright (C) 2009
// Glenn P. Downing
// ------------------------

/*
To run the tests:
    % g++ -ansi -pedantic -lcppunit -ldl -Wall -DTEST main.c++ -o main.app
    % valgrind main.app

To run the program:
    % g++ -ansi -pedantic -Wall main.c++ -o main.app
    % valgrind main.app > Darwin.out

To configure Doxygen:
    doxygen -g
That creates the file Doxyfile.
Make the following edits:
    EXTRACT_ALL            = YES
    EXTRACT_PRIVATE        = YES
    EXTRACT_STATIC         = YES
    GENERATE_LATEX         = NO

To document the program:
    doxygen Doxyfile
*/

// --------
// includes
// --------

#include <cassert>   // assert
#include <iostream>  // cout, endl
#include <stdexcept> // invalid_argument, out_of_range
#include <cstdlib>
#include <time.h>
#include "Darwin.h"

#ifdef TEST
    #include "cppunit/TestSuite.h"      // TestSuite
    #include "cppunit/TextTestRunner.h" // TestRunner

    #include "TestDarwin.h"
#endif // TEST

// ----
// main
// ----

int main () {
    using namespace std;
    ios_base::sync_with_stdio(false); // turn off synchronization with C I/O

    #ifdef TEST
        // ----------
        // unit tests
        // ----------

        CppUnit::TextTestRunner tr;
        tr.addTest(cs::TestDarwin::suite());
        tr.run();
    #else
	
	Species hopper('h',Species::getHopperInstruction());
	Species rover('r',Species::getRoverInstruction());
	Species food('f',Species::getFoodInstruction());
	Species trap('t',Species::getTrapInstruction());
	Species best('b',Species::getBestInstruction());
	srand ( time(NULL) );

	/*
	Darwin d(7,7);
	Creature c1(&hopper, 1);
	Creature c2(&hopper, 2);
	Creature c3(&hopper, 2);
	Creature c4(&rover, 3);
	Creature c5(&rover, 1);
	Creature c6(&hopper, 2);
	Creature c7(&hopper, 2);
	Creature c8(&rover, 3);
	
	d.setCreature(1,1,c2);
	d.setCreature(2,3,c3);
	d.setCreature(0,0,c4);
	d.setCreature(3,3,c1);
	d.setCreature(3,2,c5);
	d.setCreature(6,6,c6);
	d.setCreature(4,4,c7);
	d.setCreature(5,5,c8);
	d.eval(10);
	*/
        // -------
        // program
        // -------

        // ----
        // food
        // ----

        /*
         0: left
         1: go 0
        */

        // ------
        // hopper
        // ------

        /*
         0: hop
         1: go 0
        */

        // -----
        // rover
        // -----

        /*
         0: if_enemy 9
         1: if_empty 7
         2: if_random 5
         3: left
         4: go 0
         5: right
         6: go 0
         7: hop
         8: go 0
         9: infect
        10: go 0
        */

        // ----
        // trap
        // ----

        /*
         0: if_enemy 3
         1: left
         2: go 0
         3: infect
         4: go 0
        */

        // ----------
        // darwin 8x8
        // ----------

        try {
            cout << "*** Darwin 8x8 ***" << endl;
            /*
            8x8 Darwin
            Food,   facing east,  at (0, 0)
            Hopper, facing north, at (3, 3)
            Hopper, facing east,  at (3, 4)
            Hopper, facing south, at (4, 4)
            Hopper, facing west,  at (4, 3)
            Food,   facing north, at (7, 7)
            Simulate 5 moves.
            Print every grid.
            */
	    Darwin d1(8,8);
	    Creature h1(&hopper,0);
	    Creature h2(&hopper,1);
	    Creature h3(&hopper,2);
	    Creature h4(&hopper,3);
	    Creature f1(&food,1);
	    Creature f2(&food,0);
	    d1.setCreature(3,3,h1);
	    d1.setCreature(3,4,h2);
	    d1.setCreature(4,4,h3);
	    d1.setCreature(4,3,h4);
	    d1.setCreature(0,0,f1);
	    d1.setCreature(7,7,f2);
	    d1.eval(5);
            }
        catch (const invalid_argument&) {
            assert(false);}
        catch (const out_of_range&) {
            assert(false);}

        // ----------
        // darwin 7x9
        // ----------

        try {
            cout << "*** Darwin 7x9 ***" << endl;
            /*
            7x9 Darwin
            Trap,   facing south, at (0, 0)
            Hopper, facing east,  at (3, 2)
            Rover,  facing north, at (5, 4)
            Trap,   facing west,  at (6, 8)
            Simulate 5 moves.
            Print every grid.
            */
	    Darwin d(7,9);
	    Creature t1(&trap,2);
	    Creature h1(&hopper,1);
	    Creature r1(&rover,0);
	    Creature t2(&trap,3);
	    d.setCreature(0,0,t1);
	    d.setCreature(3,2,h1);
	    d.setCreature(5,4,r1);
	    d.setCreature(6,8,t2);
	    d.eval(5);
            }
        catch (const invalid_argument&) {
            assert(false);}
        catch (const out_of_range&) {
            assert(false);}

        // ------------
        // darwin 72x72
        // without best
        // ------------

        try {
            cout << "*** Darwin 72x72 without Best ***" << endl;
            /*
            Randomly place the following creatures facing randomly.
            Call rand(), mod it with 5184 (72x72), and use that for the position
            in a row-major order grid.
            Call rand() again, mod it with 4 and use that for it's direction with
            the ordering: west, north, east, south.
            Do that for each kind of creature.
            10 Food
            10 Hopper
            10 Rover
            10 Trap
            Simulate 1000 moves.
            Print every 100th grid.
            */
	    Darwin d2(72,72);
		int spot=0;
	std::vector<Creature> h(10);
	std::vector<Creature> f(10);
	std::vector<Creature> r(10);
	std::vector<Creature> t(10);
		
	    for(int x=0;x<10;++x){
		spot=std::rand()%5024;
		h[x].setRand(&hopper);
		d2.setCreature(spot/72,spot%72,h[x]);	
		}
	
	for(int x=0;x<10;++x){
		spot=std::rand()%5024;
		t[x].setRand(&trap);
		d2.setCreature(spot/72,spot%72,t[x]);	
		}
	
	
	for(int x=0;x<10;++x){
		spot=std::rand()%5024;
		f[x].setRand(&food);
		d2.setCreature(spot/72,spot%72,f[x]);
		}
	
	for(int x=0;x<10;++x){
		spot=std::rand()%5024;
		r[x].setRand(&rover);
		d2.setCreature(spot/72,spot%72,r[x]);	
		}

	d2.eval(1000);
            }
        catch (const invalid_argument&) {
            assert(false);}
        catch (const out_of_range&) {
            assert(false);}

        // ------------
        // darwin 72x72
        // with best
        // ------------

        try {
            cout << "*** Darwin 72x72 with Best ***" << endl;
            /*
            Randomly place the following creatures facing randomly.
            Call rand(), mod it with 5184 (72x72), and use that for the position
            in a row-major order grid.
            Call rand() again, mod it with 4 and use that for it's direction with
            the ordering: west, north, east, south.
            Do that for each kind of creature.
            10 Food
            10 Hopper
            10 Rover
            10 Trap
            10 Best
            Simulate 1000 moves.
            Print every 100th grid.
            */

		 Darwin d2(72,72);
		int spot=0;
		std::vector<Creature> h(10);
		std::vector<Creature> f(10);
		std::vector<Creature> r(10);
		std::vector<Creature> t(10);
		std::vector<Creature> b(10);
			
		for(int x=0;x<10;++x){
			spot=std::rand()%5024;
			h[x].setRand(&hopper);
			d2.setCreature(spot/72,spot%72,h[x]);	
			}
		
		for(int x=0;x<10;++x){
			spot=std::rand()%5024;
			t[x].setRand(&trap);
			d2.setCreature(spot/72,spot%72,t[x]);	
			}
		
		
		for(int x=0;x<10;++x){
			spot=std::rand()%5024;
			f[x].setRand(&food);
			d2.setCreature(spot/72,spot%72,f[x]);
			}
		
		for(int x=0;x<10;++x){
			spot=std::rand()%5024;
			r[x].setRand(&rover);
			d2.setCreature(spot/72,spot%72,r[x]);	
			}

		for(int x=0;x<10;++x){
			spot=std::rand()%5024;
			b[x].setRand(&best);
			d2.setCreature(spot/72,spot%72,b[x]);	
			}
	
		d2.eval(1000);
            }
	
        catch (const invalid_argument&) {
            assert(false);}
        catch (const out_of_range&) {
            assert(false);}
    #endif // NDEBUG

    return 0;}

